﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.UI;
#endif

namespace Jinndev.UI {

    /// <summary>
    /// 包含1个Canvas，分Bottom、Middle、Top共3层，其中Bottom和Top可以指定有多层。
    /// 普通UI默认创建在Middle层，可以通过UIParam指定UI行为和样式。
    /// </summary>
    public class UIManager : MonoBehaviour {

        #region static
        public static readonly string PATH = "Jinndev/Prefab/UI/UIManager";

        public static UIManager Instance { get; private set; }

        public static UIManager Create(Object parent = null, string path = null) {
            return CommonUtil.Instantiate<UIManager>(PATH, parent, path);
        }


        #endregion


        public enum LayerType {
            Top, Middle, Bottom
        }

        public event System.Action<LayerType, GameObject> onCreateLayer;
        public event System.Action<LayerType> onDestroyLayer;

        public GameObject layerPrefab;
        public Canvas canvas;
        public GameObject inputMask;
        public EventSystem eventSystem;


        /// <summary>
        /// 是否初始化完成
        /// </summary>
        public bool Initialized { get; private set; }
        /// <summary>
        /// UI摄像机
        /// </summary>
        public Camera WorldCamera { get; private set; }
        /// <summary>
        /// 设计分辨率，游戏内Canvas都是基于设计分辨率设置缩放
        /// </summary>
        public Vector2 ReferenceResolution { get; private set; }
        /// <summary>
        /// 根据设计分辨率和实际屏幕大小，计算得到的UI缩放值
        /// </summary>
        public float UIScale { get; private set; } = 1f;
        /// <summary>
        /// 实际屏幕大小
        /// </summary>
        public Vector2 ScreenSize => new Vector2(Screen.width, Screen.height);
        /// <summary>
        /// 经缩放后的UI屏幕实际大小
        /// </summary>
        public Vector2 ScreenUISize { get; private set; }

        private RectTransform[] bottoms;
        private RectTransform middle;
        private RectTransform[] tops;

        private List<UILayer> uiLayers = new List<UILayer>(); // 保存middle中的layer
        private int blockInputCount = 0;

        private BaseInputModule inputModule;
        private CanvasScaler canvasScaler;

        public BaseInputModule InputModule => inputModule;

        private void Awake() {
            Instance = this;
            canvasScaler = canvas.GetComponent<CanvasScaler>();

            if (EventSystem.current == null) {
                Debug.Log($"[{nameof(UIManager)}] EventSystem not found, create default.");
                var obj = new GameObject("EventSystem");
                obj.SetActive(false);
                //obj.AddComponent<EventSystem>();

#if ENABLE_INPUT_SYSTEM
                inputModule = obj.AddComponent<InputSystemUIInputModule>();
#else
                inputModule = obj.AddComponent<StandaloneInputModule>();
#endif
                obj.SetActive(true);
            }
            else {
                inputModule = EventSystem.current.GetComponent<BaseInputModule>();
            }
            eventSystem = EventSystem.current;

#if ENABLE_INPUT_SYSTEM
            if (inputModule is InputSystemUIInputModule) {
                (inputModule as InputSystemUIInputModule).cancel.action.performed += ctx => OnEscape(); // 绑定返回键操作
            }
            else {
                Debug.LogError($"InputModule({inputModule.GetType()}) isn't <InputSystemUIInputModule>");
            }
#endif
        }

        private void Update() {
#if !ENABLE_INPUT_SYSTEM
            if (Input.GetKeyUp(KeyCode.Escape)) {
                OnEscape();
            }
#endif
        }

        private void OnDestroy() {
            Instance = null;
            Initialized = false;
        }

        public void Init<T>(Camera worldCamera, int topCount = 1, int bottomCount = 1) where T : IReferenceResolutionProvider {
            Init<T>(worldCamera, canvas.renderMode, topCount, bottomCount);
        }

        public void Init<T>(Camera worldCamera, RenderMode renderMode, int topCount = 1, int bottomCount = 1) where T : IReferenceResolutionProvider {
            Vector2 referenceResolution = new Vector2(1920, 1080);
            try {
                System.Type providerType = typeof(T);
                IReferenceResolutionProvider provider = System.Activator.CreateInstance(providerType) as IReferenceResolutionProvider;
                referenceResolution = provider.ReferenceResolution;
            }
            catch (System.Exception e) {
                Debug.LogException(e);
            }
            Init(worldCamera, renderMode, referenceResolution, topCount, bottomCount);
        }

        public void Init(Camera worldCamera, Vector2 referenceResolution, int topCount = 1, int bottomCount = 1) {
            Init(worldCamera, canvas.renderMode, referenceResolution, topCount, bottomCount);
        }

        public void Init(Camera worldCamera, RenderMode renderMode, Vector2 referenceResolution, int topCount = 1, int bottomCount = 1) {
            WorldCamera = worldCamera;
            ReferenceResolution = referenceResolution;
            UIScale = ReferenceResolution.x / Screen.width;
            ScreenUISize = ScreenSize * UIScale;

            canvas.renderMode = renderMode;
            canvas.worldCamera = worldCamera;
            canvas.planeDistance = 5f;
            canvasScaler.referenceResolution = referenceResolution;

            bottoms = new RectTransform[bottomCount];
            for (int i = 0; i < bottomCount; i++) {
                bottoms[i] = CreateGroup("Bottom_" + i);
            }

            middle = CreateGroup("Middle");

            tops = new RectTransform[topCount];
            for (int i = 0; i < topCount; i++) {
                tops[i] = CreateGroup("Top_" + i);
            }

            inputMask.SetActive(false);
            inputMask.transform.SetAsLastSibling();
            Initialized = true;
        }

        public void SetWorldCamera(Camera camera) {
            WorldCamera = camera;
            canvas.worldCamera = camera;
        }

        #region Middle
        public void Add(GameObject obj, UIParam param) {
            // 创建一个UILayer来包裹住obj
            GameObject child = Instantiate(layerPrefab, middle, false);
            UILayer layer = child.GetComponent<UILayer>();

            layer.Init(param, obj);
            //layer.onInvokeDestroy += () => { OnLayerDestroy(layer); };
            DestroyCallback.Add(layer.gameObject, () => { OnLayerDestroy(layer); }, "UILayer.OnChildDestroy");

            uiLayers.Add(layer);
            if (uiLayers.Count > 1) {
                UILayer priorLayer = uiLayers[uiLayers.Count - 2];
                try {
                    priorLayer.OnCover();
                    // 遮挡
                    if (layer.Param.block) {
                        for (int i = uiLayers.Count - 2; i >= 0; i--) {
                            uiLayers[i].gameObject.SetActive(false);
                            uiLayers[i].OnDeactivation();
                            if (uiLayers[i].Param.block) {
                                break;
                            }
                        }
                    }
                    // 避免多层mask
                    else if (layer.Param.mask && priorLayer.Param.mask) {
                        priorLayer.ToggleMask(false);
                    }
                }
                catch (System.Exception e) {
                    Debug.LogException(e);
                }
            }

            onCreateLayer?.Invoke(LayerType.Middle, obj);
        }

        public T Add<T>(GameObject obj, UIParam param) where T : Component {
            Add(obj, param);
            return obj.GetComponent<T>();
        }

        public GameObject Add(string prefabPath, UIParam param) {
            GameObject obj = CommonUtil.Instantiate(prefabPath);
            Add(obj, param);
            return obj;
        }

        public T Add<T>(string prefabPath, UIParam param) where T : Component {
            GameObject obj = Add(prefabPath, param);
            return obj.GetComponent<T>();
        }
        #endregion

        #region Bottom
        public void AddBottom(GameObject obj, int layerIndex = 0) {
            obj.transform.SetParent(bottoms[layerIndex], false);
            DestroyCallback.Add(obj, () => {
                onDestroyLayer?.Invoke(LayerType.Bottom);
            }, "UIManager.DestroyBottom");
            onCreateLayer?.Invoke(LayerType.Bottom, obj);
        }

        public GameObject AddBottom(string prefabPath, int layerIndex = 0) {
            GameObject obj = CommonUtil.Instantiate(prefabPath);
            AddBottom(obj, layerIndex);
            return obj;
        }

        public T AddBottom<T>(string prefabPath, int layerIndex = 0) where T : Component {
            GameObject obj = AddBottom(prefabPath, layerIndex);
            return obj == null ? null : obj.GetComponent<T>();
        }

        public void DestroyBottoms() {
            foreach (RectTransform layer in bottoms) {
                for (int i = layer.childCount - 1; i >= 0; i--) {
                    Destroy(layer.GetChild(i).gameObject);
                }
            }
        }
        #endregion

        #region Top
        public void AddTop(GameObject obj, int layerIndex = 0) {
            obj.transform.SetParent(tops[layerIndex], false);
            DestroyCallback.Add(obj, () => {
                onDestroyLayer?.Invoke(LayerType.Top);
            }, "UIManager.DestroyTop");
            onCreateLayer?.Invoke(LayerType.Top, obj);
        }

        public GameObject AddTop(string prefabPath, int layerIndex = 0) {
            GameObject obj = CommonUtil.Instantiate(prefabPath);
            AddTop(obj, layerIndex);
            return obj;
        }

        public T AddTop<T>(string prefabPath, int layerIndex = 0) where T : Component {
            GameObject obj = AddTop(prefabPath, layerIndex);
            return obj == null ? null : obj.GetComponent<T>();
        }

        public void DestroyTops() {
            foreach (RectTransform layer in tops) {
                for (int i = layer.childCount - 1; i >= 0; i--) {
                    Destroy(layer.GetChild(i).gameObject);
                }
            }
        }
        #endregion

        public int MiddleLayerCount {
            get {
                return uiLayers.Count;
            }
        }

        public Vector2 ScreenToAnchoredPosition(Vector2 screenPosition) {
            if (canvasScaler.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize) {
                return (screenPosition - ScreenSize * 0.5f) * UIScale;
            }
            else {
                return screenPosition - ScreenSize * 0.5f;
            }
        }

        public Vector2 WorldToAnchoredPosition(Vector3 worldPosition) {
            if (WorldCamera != null) {
                Vector2 screenPosition = WorldCamera.WorldToScreenPoint(worldPosition);
                return ScreenToAnchoredPosition(screenPosition);
            }
#if UNITY_EDITOR
            Debug.LogError("WorldCamera is null");
#endif
            return Vector2.zero;
        }

        private void OnLayerDestroy(UILayer layer) {
            if (CommonUtil.IsQuitting) {
                return;
            }
            for (int i = uiLayers.Count - 1; i >= 0; i--) {
                if (uiLayers[i] == layer) {
                    uiLayers.RemoveAt(i);
                    break;
                }
            }

            if (uiLayers.Count > 0) {
                UILayer priorLayer = uiLayers[uiLayers.Count - 1];
                try {
                    if (layer.Param.block) {
                        // 恢复遮挡
                        for (int i = uiLayers.Count - 1; i >= 0; i--) {
                            uiLayers[i].gameObject.SetActive(true);
                            uiLayers[i].OnActivation();
                            if (uiLayers[i].Param.block) {
                                break;
                            }
                        }
                    }
                    else if (layer.Param.mask && priorLayer.Param.mask) {
                        // 恢复mask
                        priorLayer.ToggleMask(true);
                    }
                    priorLayer.OnTop();
                }
                catch (System.Exception e) {
                    Debug.LogException(e);
                }
            }

            onDestroyLayer?.Invoke(LayerType.Middle);
        }

        private RectTransform CreateGroup(string name) {
            GameObject obj = new GameObject(name, new System.Type[] { typeof(RectTransform) });
            RectTransform transform = obj.GetComponent<RectTransform>();
            transform.SetParent(canvas.transform, false);
            transform.anchorMin = Vector2.zero;
            transform.anchorMax = Vector2.one;
            transform.offsetMin = Vector2.zero;
            transform.offsetMax = Vector2.zero;
            return transform;
        }

        /// <summary>
        /// 消除指定UI之后的所有UILayer
        /// </summary>
        public void DestroyAfter(GameObject obj) {
            for (int i = uiLayers.Count - 1; i >= 0; i--) {
                UILayer layer = uiLayers[i];
                if (layer.Child != obj) {
                    layer.Destroy();
                }
                else {
                    break;
                }
            }
        }

        /// <summary>
        /// 消除指定UI之后的所有UILayer
        /// </summary>
        public void DestroyAfter<T>() where T : MonoBehaviour {
            for (int i = uiLayers.Count - 1; i >= 0; i--) {
                UILayer layer = uiLayers[i];
                if (layer.Child.GetComponent<T>() == null) {
                    layer.Destroy();
                }
                else {
                    break;
                }
            }
        }

        public void DestroyAll() {
            for (int i = uiLayers.Count - 1; i >= 0; i--) {
                UILayer layer = uiLayers[i];
                layer.Destroy();
            }
        }

        /// <summary>
        /// 设置/获得是否阻挡输入，可叠加调用设置
        /// </summary>
        public bool BlockInput {
            get {
                return blockInputCount > 0;
            }
            set {
                blockInputCount += value ? 1 : -1;
                inputModule.enabled = blockInputCount == 0;
                inputMask.SetActive(blockInputCount > 0); // 因为可能存在其他输入方式，所以加一个全屏遮罩
            }
        }

        public Coroutine BlockInputForSeconds(float seconds, System.Action callback) {
            return StartCoroutine(BlockInputForSecondsCoroutine(seconds, callback));
        }

        private IEnumerator BlockInputForSecondsCoroutine(float seconds, System.Action callback) {
            BlockInput = true;
            yield return new WaitForSeconds(seconds);
            BlockInput = false;
            callback?.Invoke();
        }

        /// <summary>
        /// 点击返回键
        /// </summary>
        private void OnEscape() {
            if (BlockInput) {
                return;
            }
            // 优先处理带有IEscape的Top
            for (int i = tops.Length - 1; i >= 0; i--) {
                RectTransform top = tops[i];
                for (int j = top.childCount - 1; j >= 0; j--) {
                    Transform child = top.GetChild(j);
                    IEscape escape = child.GetComponent<IEscape>();
                    if (escape != null) {
                        EscapeResult result = escape.OnEscape();
                        if (result == EscapeResult.Destroy) {
                            Destroy(child.gameObject);
                            return;
                        }
                        else if (result == EscapeResult.Cancel) {
                            return;
                        }
                    }
                }
            }

            // 处理最顶层的UILayer
            if (uiLayers.Count == 0) {
                return;
            }

            UILayer layer = uiLayers[uiLayers.Count - 1];
            try {
                EscapeResult result = layer.OnEscape();
                if (result == EscapeResult.Destroy) {
                    layer.Destroy();
                }
            }
            catch (System.Exception e) {
                Debug.LogException(e);
            }
        }

    }

    /// <summary>
    /// 实现此接口，可自动为UIManager设置设计分辨率
    /// </summary>
    public interface IReferenceResolutionProvider {

        /// <summary>
        /// 设计分辨率，游戏内Canvas都是基于设计分辨率设置缩放
        /// </summary>
        Vector2 ReferenceResolution { get; }

    }

}
